package work.linruchang.util;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 增强注解工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/01/24
 * @since 1.8
 **/
public class EnhanceAnnotationUtil extends AnnotationUtil {

    /**
     * 设置注解的某个字段的值
     *
     * @param annotation 注解
     * @param fieldName  字段
     * @param fieldValue 新的字段值
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T setAnnotationFieldValue(T annotation, String fieldName, Object fieldValue) {
        if (ObjectUtil.isAllNotEmpty(annotation, fieldName)) {
            Map annotationFieldValueMap = (Map) ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(annotation, "h"), "memberValues");
            if (annotationFieldValueMap.containsKey(fieldName)) {
                annotationFieldValueMap.put(fieldName, fieldValue);
            }
        }
        return annotation;
    }

    /**
     * 获取注解的某个字段值
     *
     * @param annotation 注解
     * @param fieldName  字段名
     * @param <T>
     * @return
     */
    public static <T extends Annotation> Object getAnnotationFieldValue(T annotation, String fieldName) {
        Object result = null;
        if (ObjectUtil.isAllNotEmpty(annotation, fieldName)) {
            Map annotationFieldValueMap = (Map) ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(annotation, "h"), "memberValues");
            result = annotationFieldValueMap.get(fieldName);
        }
        return result;
    }

    /**
     * 复制注解的字段值
     *
     * @param annotation      目标注解
     * @param sourceFieldName 源值字段名
     * @param targetFieldName 目标字段名
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T copyAnnotationFieldValue(T annotation, String sourceFieldName, String targetFieldName) {
        if (ObjectUtil.isAllNotEmpty(annotation, sourceFieldName, targetFieldName)) {
            Map annotationFieldValueMap = (Map) ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(annotation, "h"), "memberValues");
            annotationFieldValueMap.put(targetFieldName, annotationFieldValueMap.get(sourceFieldName));
        }
        return annotation;
    }

    /**
     * 如果目标字段值是空则拷贝源字段的属性值
     * @param annotation 注解
     * @param sourceFieldName 源值字段名
     * @param targetFieldName 目标字段名
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T emptyToCopyAnnotationFieldValue(T annotation, String sourceFieldName, String targetFieldName) {
        if (ObjectUtil.isAllNotEmpty(annotation, sourceFieldName, targetFieldName)) {
            if(ObjectUtil.isEmpty(getAnnotationFieldValue(annotation,targetFieldName))) {
                copyAnnotationFieldValue(annotation,sourceFieldName,targetFieldName);
            }
        }
        return annotation;
    }


    /**
     * 注解的值转成Map
     *
     * @param currentAnnotation 注解实例
     * @return
     */
    public static Map<String, Object> getAnnotationValueMap(Annotation currentAnnotation) {

        return Optional.ofNullable(currentAnnotation)
                .map(annotation -> {
                    Method[] objectMethods = ReflectUtil.getMethods(Object.class);
                    final List<Method> methods = ReflectUtil.getPublicMethods(annotation.getClass(), t -> {
                        if (ArrayUtil.isEmpty(t.getParameterTypes())) {
                            // 只读取无参方法
                            final String name = t.getName();
                            return  (false == "hashCode".equals(name))
                                    && (false == "toString".equals(name))
                                    && (false == "annotationType".equals(name))
                                    && (false == ArrayUtil.contains(objectMethods, t));
                        }
                        return false;
                    });


                    final HashMap<String, Object> result = new HashMap<>(methods.size(), 1);
                    for (Method method : methods) {
                        result.put(method.getName(), ReflectUtil.invoke(annotation, method));
                    }
                    return result;
                }).orElse(new HashMap<>());
    }

}
